use hash_computation::{superfeature::FinesseGroup, *};
use serde::{Deserialize, Serialize};

pub trait Resemblance: Clone + Copy {
    fn is_sequential(&self) -> bool;
    fn sf_len(&self) -> usize;
    fn calculate(&self, data:&[u8], resemblance:&mut [u64]);
}

#[impl_enum::with_methods {
    pub fn is_sequential(&self) -> bool
    pub fn sf_len(&self) -> usize
    pub fn calculate(&self, data:&[u8], resemblance:&mut [u64])
}]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ResemblanceConfig {
    None(NoneResemblance),
    Ntransform(Ntransform),
    Odess(Odess),
    Finesse(Finesse),
}

impl Default for ResemblanceConfig {
    fn default() -> Self {
        Self::Ntransform(Ntransform)
    }
}

impl ResemblanceConfig {
    pub fn sf_of(&self, data:&[u8]) -> Vec<u64> {
        let mut sf = vec![0;self.sf_len()];
        self.calculate(data, &mut sf);
        sf
    }
}

pub type NoneResemblance = ();

impl Resemblance for NoneResemblance {
    fn is_sequential(&self) -> bool { true }
    fn sf_len(&self) -> usize { 0 }
    fn calculate(&self, _data:&[u8], _resemblance:&mut [u64]) { }
}

#[derive(Debug, Clone, Copy, Serialize)]
pub struct Ntransform;

impl Resemblance for Ntransform {
    fn is_sequential(&self) -> bool { true }
    fn sf_len(&self) -> usize { 3 }
    fn calculate(&self, data:&[u8], resemblance:&mut [u64]) {
        resemblance.copy_from_slice(&superfeature::super_feature(data));
    }
}

impl<'de> Deserialize<'de> for Ntransform {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self)
    }
}


#[derive(Debug, Clone, Copy, Serialize)]
pub struct Odess;
impl Resemblance for Odess {
    fn is_sequential(&self) -> bool { true }
    fn sf_len(&self) -> usize { 3 }
    fn calculate(&self, data:&[u8], resemblance:&mut [u64]) {
        resemblance.copy_from_slice(&superfeature::odess(data));
    }
}

impl<'de> Deserialize<'de> for Odess {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self)
    }
}


#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Finesse(FinesseGroup);

impl Resemblance for Finesse {
    fn is_sequential(&self) -> bool { 
        match self.0 {
            FinesseGroup::UnsortedFourFeature => false,
            FinesseGroup::FiveFeature => false,
            _ => true,
        }
    }
    fn sf_len(&self) -> usize { 3 }
    fn calculate(&self, data:&[u8], resemblance:&mut [u64]) {
        resemblance.copy_from_slice(&superfeature::finesse(data, self.0));
    }
}